#include "ntdproto.h"
#include <sstream>

const int32 NtdSeq::m_iSeqNoTH = 0x3FFFFFFF;
const int32 NtdSeq::m_iMaxSeqNo = 0x7FFFFFFF;

#define STREAM_IN_32(buf, data)\
do{\
	assert(sizeof(data) == 4);\
	uint32 t = htonl(data);\
	int ret = evbuffer_add(buf, (void*)&t, 4);\
	if( ret == -1 ) return CODEC_ERROR;\
}while(0)

#define STREAM_IN_16(buf, data)\
do{\
	assert(sizeof(data) == 2);\
	uint16 t = htons(data);\
	int ret = evbuffer_add(buf, &t, 2);\
	if( ret == -1 ) return CODEC_ERROR;\
}while(0)

#define STREAM_IN_8(buf, data)\
do{\
	assert(sizeof(data) == 1);\
	int ret = evbuffer_add(buf, (void*)&data, 1);\
	if( ret == -1 ) return CODEC_ERROR;\
}while(0)

#define STREAM_IN_BUF(buf, data, len)\
do{\
    if( data != NULL && len > 0 ){\
	    int ret = evbuffer_add(buf, data, len);\
		if( ret == -1 ) return CODEC_ERROR;\
	}\
}while(0)

#define STREAM_OUT_32(buf, data)\
do{\
	assert(sizeof(data) == 4);\
	uint32 t = 0;\
	int ret = evbuffer_remove(buf, &t, 4);\
	if( ret == -1 ) return CODEC_ERROR;\
	data = ntohl(t);\
}while(0)

#define STREAM_OUT_16(buf, data)\
do{\
	assert(sizeof(data) == 2);\
	uint16 t = 0;\
	int ret = evbuffer_remove(buf, &t, 2);\
	if( ret == -1 ) return CODEC_ERROR;\
	data = ntohs(t);\
}while(0)

#define STREAM_OUT_8(buf, data)\
do{\
	assert(sizeof(data) == 1);\
	int ret = evbuffer_remove(buf, &data, 1);\
	if( ret == -1 ) return CODEC_ERROR;\
}while(0)

#define STREAM_OUT_BUF(buf, data, len)\
do{\
	int ret = 0;\
	if( data != NULL ){\
        ret = evbuffer_remove(buf, data, len);\
	}else{\
	    ret = evbuffer_drain(buf, len);\
	}\
	if( ret == -1 ) return CODEC_ERROR;\
}while(0)


CodecStatus NtdProtoHead::EncodeHead(evbuffer* buf)
{
	STREAM_IN_32(buf, m_packetSize);
	uint8 str_ver_type = (m_version<<4)|(m_type&0x0f);
	STREAM_IN_8(buf, str_ver_type);
	STREAM_IN_8(buf, m_reserv);
	STREAM_IN_16(buf, m_convId);

	return CODEC_SUCCESS;
}

CodecStatus NtdProtoHead::DecodeHead(evbuffer* buf)
{
	uint32 ptk_size = 0;
	ev_ssize_t len = evbuffer_copyout(buf, &ptk_size, sizeof(ptk_size));
	if( len != sizeof(m_packetSize) ){
		return CODEC_NOLEN;
	}

	m_packetSize = ntohl(ptk_size);

	if( evbuffer_get_length(buf) < m_packetSize){
		return CODEC_NOLEN;
	}

	evbuffer_drain(buf, sizeof(m_packetSize));
	//STREAM_OUT_32(buf, m_packetSize);
	uint8 str_ver_type = 0;
	STREAM_OUT_8(buf, str_ver_type);
	m_type = str_ver_type & 0x0f;
	m_version = str_ver_type>>4;
	STREAM_OUT_8(buf, m_reserv);
	STREAM_OUT_16(buf, m_convId);

	return CODEC_SUCCESS;
}

///////////////////////////NtdProtoHandShake///////////////////////////////////////////////

CodecStatus NtdProtoHandShake::Encode(evbuffer* buf)
{
	NtdProtoHead head;
	head.m_type = NtdProtoHead::HANDSHAKE;
	head.m_packetSize = NtdProtoHead::HEAD_LEN + sizeof(m_ini_seq);
	head.m_convId = m_convId;
	if(m_bPacketPair){
		head.m_reserv |= 0x4;
	}
	head.EncodeHead(buf);

	STREAM_IN_32(buf, m_ini_seq);

	return CODEC_SUCCESS;
}

CodecStatus NtdProtoHandShake::Decode(evbuffer* buf)
{
	STREAM_OUT_32(buf, m_ini_seq);
	return CODEC_SUCCESS;
}

std::string NtdProtoHandShake::Desc()
{
	std::ostringstream os;
	os << "[Handshake] conv_id = "<< m_convId  << " initial seq = " << m_ini_seq;
	return os.str().c_str();
}

uint32 NtdProtoHandShake::GetPaketSize()
{
	return NtdProtoHead::HEAD_LEN + sizeof(m_ini_seq);
}

///////////////////////////NtdProtoHandShakeResp///////////////////////////////////////////////

CodecStatus NtdProtoHandShakeResp::Encode(evbuffer* buf)
{
	NtdProtoHead head;
	head.m_type = NtdProtoHead::HANDSHAKERESP;
	head.m_packetSize = NtdProtoHead::HEAD_LEN;
	head.m_convId = m_convId;
	head.EncodeHead(buf);

	STREAM_IN_32(buf, m_ini_seq);
	STREAM_IN_32(buf, m_load);

	return CODEC_SUCCESS;
}

CodecStatus NtdProtoHandShakeResp::Decode(evbuffer* buf)
{
	STREAM_OUT_32(buf, m_ini_seq);
	STREAM_OUT_32(buf, m_load);

	return CODEC_SUCCESS;
}

std::string NtdProtoHandShakeResp::Desc()
{
	std::ostringstream os;
	os << "[HandShakeResp] conv_id = "<< m_convId << " initial seq = " << m_ini_seq <<" load = "<<m_load;
	return os.str().c_str();
}

uint32 NtdProtoHandShakeResp::GetPaketSize()
{
	return NtdProtoHead::HEAD_LEN + sizeof(m_ini_seq) + sizeof(m_load);
}

///////////////////////////NtdProtoFin///////////////////////////////////////////////

CodecStatus NtdProtoFin::Encode(evbuffer* buf)
{
	NtdProtoHead head;
	head.m_type = NtdProtoHead::FIN;
	head.m_packetSize = NtdProtoHead::HEAD_LEN;
	head.m_convId = m_convId;
	head.EncodeHead(buf);

	return CODEC_SUCCESS;
}

CodecStatus NtdProtoFin::Decode(evbuffer* buf)
{
	return CODEC_SUCCESS;
}

std::string NtdProtoFin::Desc()
{
	std::ostringstream os;
	os << "[Fin] conv_id = "<< m_convId;
	return os.str().c_str();
}

uint32 NtdProtoFin::GetPaketSize()
{
	return NtdProtoHead::HEAD_LEN;
}

///////////////////////////NtdProtoFinResp///////////////////////////////////////////////

CodecStatus NtdProtoFinResp::Encode(evbuffer* buf)
{
	NtdProtoHead head;
	head.m_type = NtdProtoHead::FINRESP;
	head.m_packetSize = GetPaketSize();//NtdProtoHead::HEAD_LEN + sizeof(m_recv_rate) + sizeof(m_pkt_loss);
	head.m_convId = m_convId;
	if(m_bCongestion){
		head.m_reserv |= 0x8;
	}
	head.EncodeHead(buf);

	STREAM_IN_32(buf, m_recv_rate);
	STREAM_IN_32(buf, m_pkt_loss);

	return CODEC_SUCCESS;
}

CodecStatus NtdProtoFinResp::Decode(evbuffer* buf)
{
	STREAM_OUT_32(buf, m_recv_rate);
	STREAM_OUT_32(buf, m_pkt_loss);
	return CODEC_SUCCESS;
}

std::string NtdProtoFinResp::Desc()
{
	std::ostringstream os;
	os << "[FinResp] conv_id = "<< m_convId << " recv_rate = "<<byte_format(m_recv_rate,'A')<<"/s"
		<< " OWD increasing: <<" << m_bCongestion<<">>" << " pkt_loss = " <<m_pkt_loss;
	return os.str().c_str();
}

uint32 NtdProtoFinResp::GetPaketSize()
{
	return NtdProtoHead::HEAD_LEN + sizeof(m_recv_rate) + sizeof(m_pkt_loss);
}

///////////////////////////NtdProtoData///////////////////////////////////////////////

CodecStatus NtdProtoData::Encode(evbuffer* buf)
{
	NtdProtoHead head;
	head.m_type = NtdProtoHead::DATA;
	head.m_packetSize = NtdProtoHead::HEAD_LEN + sizeof(m_ptk_seq) 
		+ sizeof(m_timestamp_sending) + m_len;
	head.m_convId = m_convId;
	head.EncodeHead(buf);

	STREAM_IN_32(buf, m_ptk_seq);
	STREAM_IN_32(buf, m_timestamp_sending);
	STREAM_IN_BUF(buf, m_pData, m_len);


	return CODEC_SUCCESS;
}

CodecStatus NtdProtoData::Decode(evbuffer* buf)
{
	STREAM_OUT_32(buf, m_ptk_seq);
	STREAM_OUT_32(buf, m_timestamp_sending);
	STREAM_OUT_BUF(buf, m_pData, m_len);
	return CODEC_SUCCESS;
}

uint32 NtdProtoData::GetPaketSize()
{
	return NtdProtoHead::HEAD_LEN + sizeof(m_ptk_seq) + sizeof(m_timestamp_sending) + m_len;
}

std::string NtdProtoData::Desc()
{
	std::ostringstream os;
	os << " conv_id = "<< m_convId << " seq = " << m_ptk_seq << " ts_send = " << m_timestamp_sending;
	return os.str().c_str();
}

///////////////////////////NtdProtoFeedback///////////////////////////////////////////////

CodecStatus NtdProtoFeedback::Encode(evbuffer* buf)
{
	NtdProtoHead head;
	head.m_type = NtdProtoHead::FEEDBACK;
	head.m_packetSize = NtdProtoHead::HEAD_LEN + sizeof(m_ptk_seq)
		+ sizeof(m_timestamp_sending) + sizeof(m_timestamp_recving) 
		+ sizeof(m_recving_rate) + sizeof(m_pkt_loss) ;
	head.m_convId = m_convId;
	head.EncodeHead(buf);

	STREAM_IN_32(buf, m_ptk_seq);
	STREAM_IN_32(buf, m_timestamp_sending);
	STREAM_IN_32(buf, m_timestamp_recving);
	STREAM_IN_32(buf, m_recving_rate);
	STREAM_IN_32(buf, m_pkt_loss);
	return CODEC_SUCCESS;
}

CodecStatus NtdProtoFeedback::Decode(evbuffer* buf)
{
	STREAM_OUT_32(buf, m_ptk_seq);
	STREAM_OUT_32(buf, m_timestamp_sending);
	STREAM_OUT_32(buf, m_timestamp_recving);
	STREAM_OUT_32(buf, m_recving_rate);
	STREAM_OUT_32(buf, m_pkt_loss);
	return CODEC_SUCCESS;
}

uint32 NtdProtoFeedback::GetPaketSize()
{
	return NtdProtoHead::HEAD_LEN + sizeof(m_ptk_seq)
		+ sizeof(m_timestamp_sending) + sizeof(m_timestamp_recving) 
		+ sizeof(m_recving_rate) + sizeof(m_pkt_loss) ;
}

std::string NtdProtoFeedback::Desc()
{
	std::ostringstream os;
	os << " conv_id = "<< m_convId 
	   << " seq = " <<m_ptk_seq
	   << " ts_send = " << m_timestamp_sending
	   << " ts_recv = " << m_timestamp_recving
	   << " rel_owd = " << m_timestamp_recving - m_timestamp_sending
	   << " recv_rate = " << m_recving_rate
	   << " pkt_loss = " <<m_pkt_loss;
	return os.str().c_str();
}

///////////////////////////NtdProtoRawData///////////////////////////////////////////////

CodecStatus NtdProtoRawData::Encode(evbuffer* buf)
{
	NtdProtoHead head;
	head.m_type = NtdProtoHead::RAWDATA;
	head.m_packetSize = NtdProtoHead::HEAD_LEN + m_len;
	head.m_convId = m_convId;
	head.EncodeHead(buf);

	STREAM_IN_BUF(buf, m_pData, m_len);

	return CODEC_SUCCESS;
}

CodecStatus NtdProtoRawData::Decode(evbuffer* buf)
{
	STREAM_OUT_BUF(buf, m_pData, m_len);
	return CODEC_SUCCESS;
}

uint32 NtdProtoRawData::GetPaketSize()
{
	return NtdProtoHead::HEAD_LEN + m_len;
}

std::string NtdProtoRawData::Desc()
{
	std::ostringstream os;
	os << " conv_id = "<< m_convId;
	return os.str().c_str();
}
//////////////////////////////////////////////////////////////////////////

bool NtdProtoParser::ParseProtocol(evbuffer* buf, void* pContext)
{
	NtdProtoHead head;
	if( head.DecodeHead(buf) == CODEC_NOLEN ){
		//wait until enough head data
		return false;
	}

	NtdProto * proto = NULL;

	switch( head.m_type ){
		case NtdProtoHead::HANDSHAKE:
			{
				proto = new NtdProtoHandShake();
				proto->Decode(buf);
				proto->m_convId = head.m_convId;
				reinterpret_cast<NtdProtoHandShake*>(proto)->m_bPacketPair
					= head.m_reserv & 0x4?true:false;
				HandleHandShake(reinterpret_cast<NtdProtoHandShake*>(proto), pContext);
				break;
			}
		case NtdProtoHead::HANDSHAKERESP:
			{
				proto = new NtdProtoHandShakeResp();
				proto->Decode(buf);
				proto->m_convId = head.m_convId;
				HandleHandShakeResp(reinterpret_cast<NtdProtoHandShakeResp*>(proto), pContext);
				break;
			}
		case NtdProtoHead::FIN:
			{
				proto = new NtdProtoFin();
				proto->Decode(buf);
				proto->m_convId = head.m_convId;
				HandleFin(reinterpret_cast<NtdProtoFin*>(proto), pContext);
				break;
			}
		case NtdProtoHead::FINRESP:
			{
				proto = new NtdProtoFinResp();
				proto->Decode(buf);
				proto->m_convId = head.m_convId;
				reinterpret_cast<NtdProtoFinResp*>(proto)->m_bCongestion 
					= head.m_reserv & 0x8?true:false;
				HandleFinResp(reinterpret_cast<NtdProtoFinResp*>(proto), pContext);
				break;
			}
		case NtdProtoHead::DATA:
			{
				uint32 body_len = head.m_packetSize - NtdProtoHead::HEAD_LEN;
				proto = new NtdProtoData(body_len);
				proto->Decode(buf);
				proto->m_convId = head.m_convId;
				HandleProtoData(reinterpret_cast<NtdProtoData*>(proto), pContext);
				break;
			}
		case NtdProtoHead::FEEDBACK:
			{
				proto = new NtdProtoFeedback();
				proto->Decode(buf);
				proto->m_convId = head.m_convId;
				HandleProtoFeedback(reinterpret_cast<NtdProtoFeedback*>(proto), pContext);
				break;
			}
		case NtdProtoHead::RAWDATA:
			{
				uint32 body_len = head.m_packetSize - NtdProtoHead::HEAD_LEN;
				proto = new NtdProtoRawData(body_len);
				proto->Decode(buf);
				proto->m_convId = head.m_convId;
				HandleProtoRawData(reinterpret_cast<NtdProtoRawData*>(proto), pContext);
				break;
			}

		default:
			assert(false);
	}

	delete proto;

	return true;
}